package expression;

import java.util.ArrayList;

import runtime.TypeFactory;
import util.Result;
import util.Scope;
import util.Type;
import exception.ExpressionException;
import exception.ResultException;
import exception.TypeException;
import expression.MultiplicativeExpression.Sign;

/**
 * Klasa odpowiedzialna za przetrzymywanie wyrazenia porownania
 * 
 * @author Jakub Fibinger
 *
 */
public class EqualityExpression extends Expression {

	/**
	 * Ciag znakow rownosci badz nierownosci 
	 */
	private ArrayList<Sign> signs;
	/**
	 * Ciag wyrazen nizszego poziomu
	 */
	private ArrayList<Expression> expressions;
	private Type type;

	public static enum Sign {
		EQUAL, NOT_EQUAL;
	}

	/**
	 * @param scope Zakres zmiennych
	 * @param expression Wyrazenie nizszego poziomu
	 * @throws ExpressionException
	 */
	public EqualityExpression(Scope scope, Expression expression) throws ExpressionException {
		if (expression == null) {
			throw new ExpressionException();
		}
		signs = new ArrayList<Sign>();
		expressions = new ArrayList<Expression>();
		expressions.add(expression);
		type = expression.getResultType();
		result = new Result(type, null);
	}

	/**
	 * @param scope Zakres zmiennych
	 * @param signs Ciag znakow rownosci badz nierownosci
	 * @param expressions Ciag wyrazen nizszego poziomu
	 * @throws ExpressionException
	 */
	public EqualityExpression(Scope scope, ArrayList<Sign> signs,
			ArrayList<Expression> expressions) throws ExpressionException {
		TypeFactory tf = TypeFactory.getInstance();
		if (expressions.size() == 0) {
			throw new ExpressionException();
		} else {
			type = expressions.get(0).getResultType();
			if (expressions.size() != signs.size() + 1) {
				throw new ExpressionException();
			} else
				try {
					if (type.equals(tf.getType("double"))
							|| type.equals(tf.getType("int"))) {
						Type dType = tf.getType("double");
						Type iType = tf.getType("int");
						for (Expression expr : expressions) {
							Type tmp = expr.getResultType();
							if (!dType.equals(tmp) && !iType.equals(tmp)) {
								throw new ExpressionException();
							}
						}
					} else if (type.equals(tf.getType("string"))
							|| type.equals(tf.getType("char"))) {
						Type sType = tf.getType("string");
						Type cType = tf.getType("char");
						for (Expression expr : expressions) {
							Type tmp = expr.getResultType();
							if (!sType.equals(tmp) && !cType.equals(tmp)) {
								throw new ExpressionException();
							}
						}
					} else {
						for (Expression expr : expressions) {
							if (!type.equals(expr.getResultType())) {
								throw new ExpressionException();
							}
						}
					}
				} catch (TypeException e) {
					// zignoruj
				}
		}

		this.signs = signs;
		this.expressions = expressions;
		if (expressions.size() > 1) {
			try {
				result = new Result(tf.getType("bool"), null);
			} catch (TypeException e) {
				// olac
			}
		} else {
			result = new Result(this.type, null);
		}
	}

	/**
	 * Metoda dodajaca kolejne wyrazenie do listy wraz ze znakiem
	 * 
	 * @param expression Nowe wyrazenie nizszego poziomu
	 * @param sign Nowy znak
	 * @throws ExpressionException
	 */
	public void addExpression(Expression expression, Sign sign)
			throws ExpressionException {
		if (expression == null || sign == null) {
			throw new ExpressionException();
		}
		TypeFactory tf = TypeFactory.getInstance();
		try {
			if (type.equals(tf.getType("double"))
					|| type.equals(tf.getType("int"))) {
				Type dType = tf.getType("double");
				Type iType = tf.getType("int");
				if (!dType.equals(expression.getResultType())
						&& !iType.equals(expression.getResultType())) {
					throw new ExpressionException();
				}

			} else if (type.equals(tf.getType("string"))
					|| type.equals(tf.getType("char"))) {
				Type sType = tf.getType("string");
				Type cType = tf.getType("char");
				if (!sType.equals(expression.getResultType())
						&& !cType.equals(expression.getResultType())) {
					throw new ExpressionException();
				}
			} else {
				if (!type.equals(expression.getResultType())) {
					throw new ExpressionException();

				}
			}
			signs.add(sign);
			expressions.add(expression);
			if(expressions.size()>1) {
				result.setType(tf.getType("bool"));
			}
		} catch (TypeException e) {
			// zignoruj
		}
	}

	/* (non-Javadoc)
	 * @see expression.Expression#evaluate()
	 */
	@Override
	public Result evaluate() throws ExpressionException {
		Result tmp1, tmp2;
		tmp1 = expressions.get(0).evaluate();
		if(expressions.size()==1) {
			result.setValue(tmp1.getValue());
		} else {
			result.setValue(true);
			for (int i = 0; (Boolean) result.getValue() && i < signs.size(); i++) {
				tmp2 = expressions.get(i + 1).evaluate();
				if (signs.get(i).compareTo(Sign.EQUAL) == 0) {
					try {
						result.setValue(tmp1.compareTo(tmp2) == 0);
					} catch (ResultException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} else {
					try {
						result.setValue(tmp1.compareTo(tmp2) != 0);
					} catch (ResultException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				tmp1 = tmp2;
			}
		}
		return result;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString(){
		StringBuilder sb = new StringBuilder("Equality [ ");
		sb.append(expressions.get(0) + " ");
		int i = 1;
		for(Sign sign : signs)
			sb.append(sign + " " + expressions.get(i++) + " ");
		sb.append("]");
		return sb.toString();
	}

}
